<?php
/**
 * Created by PhpStorm.
 * User: Mysic
 * Date: 2020/10/29
 * Time: 14:43
 */

namespace app\mocks\extend;


use app\common\enum\OrderType as OrderTypeEnum;
use app\common\exception\BaseException;
use think\Env;
use think\Request;

/**
 * 支付下单，退款
 * Class MockWeChatPay
 * @package app\mocks\extend
 */
class MockWeChatPay extends MockWeChatPayBase
{
    protected static $instance;
    protected $request;
    protected $notifyParams;
    protected function __construct($setting)
    {
        parent::__construct($setting);
        $this->request = Request::instance();
    }
    public static function instance($setting = [])
    {
        if(!Env::get('mock.pay')) {
            return null;
        }
        if(self::$instance == null || (!self::$instance instanceof self)) {
            self::$instance = new self($setting);
        }
        return self::$instance;
    }

    public function setConfigArr(array $config)
    {
        $this->setting = $config;
        !empty($this->setting) && $this->setConfig($this->setting['app_id'], $this->setting['app_secret']);
    }

    public function unifiedorder($order_no, $openid, $totalFee, $orderType = OrderTypeEnum::MASTER)
    {
        $nonceStr = 'mock_unifiedorder_noncestr_' . time();
        $this->notifyParams = [
            'appid' => $this->setting['app_id'],
            'attach' => json_encode(['order_type' => $orderType]),
            'body' => $order_no,
            'mch_id' => $this->setting['mchid'],
            'nonce_str' => $nonceStr,
            'notify_url' => base_url() . 'notice.php',  // 异步通知地址
            'openid' => $openid,
            'profit_sharing'=>'Y',
            'out_trade_no' => $order_no,
            'spbill_create_ip' => \request()->ip(),
            'total_fee' => $totalFee * 100, // 价格:单位分
            'trade_type' => 'JSAPI',
        ];

        $prepayId = 'mock_unifiedorder_prepay_id' . time();

        $timestamp = time();
        $data = [
            'appId' => $this->appId,
            'nonceStr' => $nonceStr,
            'package' => 'prepay_id=' . $prepayId,
            'signType' => 'MD5',
            'timeStamp' => $timestamp,
        ];
        $paySign = $this->makeSign($data);
        return [
            'prepay_id' => $prepayId,
            'nonceStr' => $nonceStr,
            'timeStamp' => (string)$timestamp,
            'paySign' => $paySign
        ];
    }

    public function doPayNotify()
    {
        $data = [
            'appid' => $this->notifyParams['appid'],
            'attach' => $this->notifyParams['attach'],
            'bank_type' => 'MOCK',
            'fee_type' => 'CNY',
            'is_subscribe' => 'Y',
            'mch_id' => $this->notifyParams['mch_id'],
            'nonce_str' => 'mock_pay_notify_nonce_str_' . time(),
            'openid' => $this->notifyParams['openid'],
            'out_trade_no' => $this->notifyParams['out_trade_no'],
            'result_code' => 'SUCCESS',
            'return_code' => 'SUCCESS',
            'time_end' => date('YmdHis', time()),
            'total_fee' => $this->notifyParams['total_fee'],
            'coupon_fee' => $this->notifyParams['coupon_fee'] ?? '0',
            'coupon_count' => $this->notifyParams['coupon_count'] ?? '0',
            'coupon_type' => $this->notifyParams['coupon_type'] ?? '',
            'coupon_id' => $this->notifyParams['coupon_id'] ?? '',
            'trade_type' => 'JSAPI',
            'transaction_id' => 'mock_transaction_id_' . time()
        ];
        $data['sign'] = $this->makeSign($data);
        $ret = $this->post($this->notifyParams['notify_url'], $this->toXml($data));
        $ret = $this->fromXml($ret);
        if($ret['return_code'] != 'SUCCESS'|| $ret['return_msg'] != 'OK') {
            return false;
        }
        return true;
    }

    public function refund($transactionId, $totalFee, $refundFee)
    {
        $orderNo = db('order')->where('transaction_id',$transactionId)->value('order_no');
        $refundNo = date('YmdHis') . substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8).$transactionId.rand(0,9999);
        $prepay = [
            'return_code' => 'SUCCESS',
            'return_msg' => 'OK',
            'appid' => $this->setting['app_id'],
            'mch_id' => $this->setting['mchid'],
            'nonce_str' => 'mock_refund_nonce_' . time(),
            'result_code' => 'SUCCESS',//SUCCESS FAIL
//            'err_code' => 'ERROR',
//            'err_code_des' => '', // 同一笔订单的退款数量超过限制 refund_fee大于可退金额 订单已全额退款
            'transaction_id' => $transactionId,
            'out_trade_no' => $orderNo,
            'out_refund_no' => $refundNo,
            'refund_id' => 'mock_refund_id_' . time(),
            'refund_channel' => [],
            'refund_fee' => $refundFee,
            'coupon_refund_fee' => '0',
            'total_fee' =>  $totalFee,
            'cash_fee' => $totalFee,
            'coupon_refund_count' => '0',
            'cash_refund_fee' => $refundFee
        ];
        if ($prepay['return_code'] === 'FAIL') {
            throw new BaseException(['msg' => $prepay['return_msg']]);
        }
        if ($prepay['result_code'] === 'FAIL' && $prepay['err_code'] != 'FREQUENCY_LIMITED') {
            throw new BaseException(['msg' => $prepay['err_code_des']]);
        }
        if($prepay['result_code'] === 'FAIL' && $prepay['err_code'] == 'FREQUENCY_LIMITED') {
            sleep(1);
            $prepay = $this->refund($transactionId, $totalFee, $refundFee);
            if($prepay['return_code'] == 'SUCCESS' && $prepay['result_code'] == 'SUCCESS') {
                return $prepay;
            }
        }

        return $prepay;
    }

    public function refundquery()
    {
        return true;
    }

    public function getAccessToken()
    {

    }
}